/*
	drm框架下的hdmi控制，dp需要更改资源的连接器和扫描器id，dp使用crtc_id = res->crtcs[2];
	conn_id = res->connectors[2];     1是mipi屏幕
	也要更换显示模式 同样用modes[2]
	主函数需要用到
	extern int drm_hdmi_fd;
	extern uint32_t hdmi_crtc_id;
	extern struct drm_hdmi_device drm_hdmi_buf[2];
	要用extern声明


	主函数定义drmEventContext ev;		//翻页用
	赋值ev.version = DRM_EVENT_CONTEXT_VERSION;
	ev.page_flip_handler = drm_page_flip_handler;

	回调函数
	static void drm_page_flip_handler(int fd, uint32_t frame,
    uint32_t sec, uint32_t usec,
    void *data)
	{
    uint32_t crtc_id = *(uint32_t *)data;

    drmModePageFlip(drm_hdmi_fd, hdmi_crtc_id, drm_hdmi_buf[page_cnt].fb_id,
        DRM_MODE_PAGE_FLIP_EVENT, &hdmi_crtc_id);
    
    if(page_cnt == 0)
      page_cnt = 1;
    else 
      page_cnt = 0;
	}
	翻页函数只是提交翻页信号，真正的翻页会在VSYNC信号来临时自动进行
	drmHandleEvent(drm_hdmi_fd, &ev);调用此函数后会在翻页完成以及VSYNC来临时触发回调函数
	drm初始化中已经翻到了第一页，所以在capture之后使用drmHandleEvent(drm_hdmi_fd, &ev);就会在VSYNC来临时触发回调函数，在回调函数中执行翻页，计数选择哪一页
	达到了翻页的效果，避免撕裂

	for(int i = 0;i<500;i++)
    {
        ov13855_capture_one(drm_hdmi_buf[0].vaddr);
        printf("捕获一帧\n");
        drmHandleEvent(drm_hdmi_fd, &ev);
        
        ov13855_capture_one(drm_hdmi_buf[1].vaddr);
        printf("捕获一帧\n");
        drmHandleEvent(drm_hdmi_fd, &ev);
    }
    
    makefile 中要链接libdrm库
    CFLAGS = $(shell pkg-config --cflags libdrm)
	LDFLAGS = $(shell pkg-config --libs libdrm)
*/
#include "drm_hdmi_page_filp.hpp"

//static int terminate;
drmModeConnector *hdmi_conn;			//connetor相关的结构体
drmModeRes *hdmi_res;					//显示器资源
struct drm_hdmi_device drm_hdmi_buf[2];	//drm显示缓冲区
int drm_hdmi_fd;						//drm文件描述符
uint32_t hdmi_conn_id;					//连接器id
uint32_t hdmi_crtc_id;					//扫描器id

/*
功能：为hdmi创建一个framebuffer缓冲区
*/
static int drm_hdmi_create_fb(struct drm_hdmi_device *bo)
{
	/* create a dumb-buffer, the pixel format is XRGB888 */
	bo->create.width = bo->width;
	bo->create.height = bo->height;
	bo->create.bpp = 32;

	/* handle, pitch, size will be returned */
	drmIoctl(drm_hdmi_fd, DRM_IOCTL_MODE_CREATE_DUMB, &bo->create);

	/* bind the dumb-buffer to an FB object */
	bo->pitch = bo->create.pitch;
	bo->size = bo->create.size;
	bo->handle = bo->create.handle;
	drmModeAddFB(drm_hdmi_fd, bo->width, bo->height, 24, 32, bo->pitch,
			   bo->handle, &bo->fb_id);

	/*bo->offsets[0] = 0;
	bo->offsets[1] = 0;
	bo->offsets[2] = 0;
	bo->offsets[3] = 0;
	drmModeAddFB2(drm_hdmi_fd,bo->width, bo->height,DRM_FORMAT_BGRX8888,&bo->handle,&bo->pitch,bo->offsets,&bo->fb_id,0);*/
	
	//每行占用字节数，共占用字节数，MAP_DUMB的句柄
	printf("pitch = %d ,size = %d, handle = %d \n",bo->pitch,bo->size,bo->handle);

	/* map the dumb-buffer to userspace */
	bo->map.handle = bo->create.handle;
	drmIoctl(drm_hdmi_fd, DRM_IOCTL_MODE_MAP_DUMB, &bo->map);

	bo->vaddr = (uint32_t *)mmap(0, bo->create.size, PROT_READ | PROT_WRITE,
			MAP_SHARED, drm_hdmi_fd, bo->map.offset);

	/* initialize the dumb-buffer with white-color */
	memset(bo->vaddr, 0x00,bo->size);

	return 0;
}

static void drm_hdmi_destroy_fb(struct drm_hdmi_device *bo)
{
	struct drm_mode_destroy_dumb destroy = {};

	drmModeRmFB(drm_hdmi_fd, bo->fb_id);
	
	munmap(bo->vaddr, bo->size);
	
	destroy.handle = bo->handle;

	drmIoctl(drm_hdmi_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy);
}

int drm_hdmi_init(uint32_t * width,uint32_t * height)
{

	//打开drm设备，设备会随设备树的更改而改变,多个设备时，请留一下每个屏幕设备对应的drm设备
	drm_hdmi_fd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC);
    if(drm_hdmi_fd < 0){
        printf("wrong\n");
        return 0;
    }

	//获取drm的信息
	hdmi_res = drmModeGetResources(drm_hdmi_fd);
	hdmi_crtc_id = hdmi_res->crtcs[0];
	hdmi_conn_id = hdmi_res->connectors[0];
	//打印CRTCS,以及conneter的id
	printf("crtc = %d , conneter = %d\n",hdmi_crtc_id,hdmi_conn_id);

	hdmi_conn = drmModeGetConnector(drm_hdmi_fd, hdmi_conn_id);

	//选择的显示模式编号
	uint8_t conn_modes_cnt = 255;

	//打印屏幕支持的分辨率
	printf("显示器支持的显示模式个数：%d\n",hdmi_conn->count_modes);
	for (int i = 0; i < hdmi_conn->count_modes; i++)
	{
		printf("width = %d , height = %d\n",hdmi_conn->modes[i].hdisplay,hdmi_conn->modes[i].vdisplay);
		if(conn_modes_cnt == 255 && hdmi_conn->modes[i].hdisplay == 1920 && hdmi_conn->modes[i].vdisplay == 1080)
			conn_modes_cnt = i;
	}

	printf("选择的显示模式编号：%d\n",conn_modes_cnt);

	//最终选择的分辨率
	*width = hdmi_conn->modes[conn_modes_cnt].hdisplay;
	*height = hdmi_conn->modes[conn_modes_cnt].vdisplay;

	drm_hdmi_buf[0].width = hdmi_conn->modes[conn_modes_cnt].hdisplay;
	drm_hdmi_buf[0].height = hdmi_conn->modes[conn_modes_cnt].vdisplay;

	drm_hdmi_buf[1].width = hdmi_conn->modes[conn_modes_cnt].hdisplay;
	drm_hdmi_buf[1].height = hdmi_conn->modes[conn_modes_cnt].vdisplay;

	printf("选择的显示模式分辨率：width = %d , height = %d\n",drm_hdmi_buf[0].width,drm_hdmi_buf[0].height);

	//创建framebuffer层
	drm_hdmi_create_fb(&drm_hdmi_buf[0]);
	drm_hdmi_create_fb(&drm_hdmi_buf[1]);

	//设置CRTCS
	drmModeSetCrtc(drm_hdmi_fd, hdmi_crtc_id, drm_hdmi_buf[0].fb_id,
		0, 0, &hdmi_conn_id, 1, &hdmi_conn->modes[conn_modes_cnt]);//设置crtcs只需一次，crtcs的任务是连通framebuffer和显示器
		//指定初始的framebuffer，pageflip是切换crtcs使用的framebuffer

	drmModePageFlip(drm_hdmi_fd, hdmi_crtc_id, drm_hdmi_buf[0].fb_id,
		DRM_MODE_PAGE_FLIP_EVENT, &hdmi_crtc_id);//第一次先翻到第一页



	return 0;
}

void drm_hdmi_exit()
{
	drm_hdmi_destroy_fb(&drm_hdmi_buf[0]);
	drm_hdmi_destroy_fb(&drm_hdmi_buf[1]);
	drmModeFreeConnector(hdmi_conn);
	drmModeFreeResources(hdmi_res);
	close(drm_hdmi_fd);

}


